#pragma once

namespace zzz {
template<zuint N, typename T>
class SupervisedDimensionReduction
{
public:
  void Train(const zMatrixBaseR<double> &data, const vector<int> &label) {
    // normalize label, make them 0, 1, 2,...
    label_count_=0;
    for (zuint i=0; i<label; i++) {
      if (labelmap_.find(label[i])!=labelmap_.end()) {
        // label already in label map
        continue;
      } else {
        // add new normalized label in label map
        labelmap_[label[i]]=label_count_++;
      }
    }

    // assign new label
    norlabels__.clear();
    norlabels__.reserve(label.size());
    for (zuint i=0; i<label.size(); i++) {
      norlabels__[i]=labelmap_[label[i]];
    }

    // call real train function
    DoTrain(data, norlabels__);
  }

  template<zuint N2>
  void DimensionReduce(const vector<Vector<N,T> > &data, vector<Vector<N2,T> > &newdata)=0;

  }
protected:
  /// This should be implemented by derived class, can assume norlabel_ is continuous zero-based integers (0, 1, 2,..)
  void DoTrain(const vector<Vector<N,T> > &data, const vector<int> &norlabel_)=0;

  map<int, int> labelmap_;
  vector<int> norlabels__;
  int label_count_;
};
};  // namespace zzz